package com.killers.cold.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.killers.cold.config.FileUploadConfig;
import com.killers.cold.orm.entity.FileInformation;
import com.killers.cold.orm.entity.User;
import com.killers.cold.orm.entity.UserFile;
import com.killers.cold.orm.mapper.UserFileMapper;
import com.killers.cold.orm.mapper.UserMapper;
import com.killers.cold.service.IFileUploadService;
import com.killers.cold.orm.mapper.FileMapper;
import com.killers.cold.util.FileUtil;
import com.killers.cold.util.RegexUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.*;

/**
 * @author 罗天豪
 * @date 2019/9/4
 */
@Service
public class FileUploadService extends ServiceImpl<FileMapper, FileInformation> implements IFileUploadService {


    /**
     * 保存文件基本信息，避免重复使用函数，增大开销
     */
    private String fileName;
    private String fileOriginalName;
    private String fullFileName;
    private String fileType;
    private String fileMd5;


    @Autowired
    private FileMapper fileMapper;

    @Autowired
    UserFileMapper userFileMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    FileUploadConfig fileUploadConfig;

    @Override
    public FileInformation saveFile(MultipartFile file, String userId) {

        //查出用户
        User user = userMapper.selectByUserId(userId);

        if (!judgeIsFileAndIsExist(file)) {
            //保存文件，当插入数据产生异常，就删除该文件（已经传入本地的）
            try {
                if (fileTransferTo(file, getFullFileName())) {
                    FileInformation fileInformation = saveColdKillersFile(file,user);
                    //使用Mybatis Plus的CRUD;保存上传文件信息
                    fileMapper.insert(fileInformation);
                    //保存对应用户文件关系
                    userFileMapper.insert(setUserFileInfo(fileMd5,user ));
                    return fileInformation;
                }
            } catch (Exception e) {
                File newFile = new File(fileUploadConfig.getPath() + "/" + fullFileName);
                newFile.delete();
                e.printStackTrace();
            }
        }else {
            //当文件表中有这个文件，则不执行传输，只是保存用户文件关系信息即可
            UserFile userFile = setUserFileInfo(fileMd5, user);
            //当文件关系信息表中不存在此文件和用户信息，就保存用户文件关系。否则只返回该文件的查询结果
            if (StringUtils.isEmpty(userFileMapper.selectByUserIdFileId(userFile))) {
                userFileMapper.insert(userFile);
                return fileMapper.selectByFileId(fileMd5);
            }
            else {
                return fileMapper.selectByFileId(fileMd5);
            }
        }
        return null;
    }

    @Override
    public ArrayList<FileInformation> saveFiles(ArrayList<MultipartFile> files, String userId) {
        ArrayList<FileInformation> fileInformationArrayList = new ArrayList<>();
        for (MultipartFile file:files
             ) {
            fileInformationArrayList.add(saveFile(file,userId ));
        }
        return fileInformationArrayList;
    }




    /**
     * 设置用户对应文件实体
     * @param fileMd5 文件唯一标识
     * @param user 用户实体
     * @return 用户文件关系实体
     */
    private UserFile setUserFileInfo(String fileMd5,User user) {
        UserFile userFile = new UserFile();
        userFile.setFileId(fileMd5);
        userFile.setUserId(user.getUserId());
        userFile.setCreateBy(user.getUserNickname());
        return userFile;
    }

    /**
     * 存储文件的信息
     * @param file 传输文件
     * @param user 用户
     * @return 文件信息
     */
    private FileInformation saveColdKillersFile(MultipartFile file, User user) {
        FileInformation fileInformation = new FileInformation();
        fileInformation.setFileOriginalName(fileOriginalName);
        fileInformation.setFileName(fileName);
        fileInformation.setFilePath(fileUploadConfig.getPath());
        fileInformation.setFileType(fileType);
        fileInformation.setFileId(fileMd5);
        fileInformation.setEnabled((long)(1));

        fileInformation.setCreateTime(new Date());
        fileInformation.setCreateBy(user.getUserNickname());

        HashMap<String, Object> fileSizeAndUom = FileUtil.getFileSizeAndUom(file.getSize());
        fileInformation.setFileSize((BigDecimal) fileSizeAndUom.get("SIZE"));
        fileInformation.setFileSizeUom((String) fileSizeAndUom.get("UOM"));

        return fileInformation;
    }

    /**
     * 生成文件新名称，并赋给fileName字段，避免重复使用函数
     * @return 文件名字符串
     */
    private String createFileName() {
        fileName = FileUtil.createFileName();
        return fileName;
    }

    /**
     * 获取文件全名称（名称+"."+类型），并赋给fullFileName字段，避免重复使用函数
     * @return 文件全名字符串
     */
    private String getFullFileName() {
        fullFileName = createFileName() + "." + fileType;
        return fullFileName;
    }

    /**
     * 传输文件，并返回真值
     * @param file 传入文件
     * @param fullFileName 完整文件名
     * @return 如果传输文件成功返回true
     */
    private Boolean fileTransferTo(MultipartFile file, String fullFileName) {
        if (fullFileName != null) {
            try {
                //包含将来传入文件全路径名的File对象
                File newFile = new File(fileUploadConfig.getPath() + "/" + fullFileName);
                //判断文件路径是否存在
                if (!newFile.getParentFile().exists()) {
                    newFile.getParentFile().mkdir();
                }
                file.transferTo(newFile);
                return true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }


    /**
     * 判断传入文件是否已经被存储
     * @param file 传入文件
     * @return 如果本地已经存储了返回true，没有存储返回false
     */
    private Boolean judgeIsFileAndIsExist(MultipartFile file) {
        if (!file.isEmpty()) {
            fileType = getFileType(file);
            fileMd5 = getFileMd5(file);
            fileOriginalName = getFileOriginalName(file);
            if (fileType != null && fileMd5 != null && fileOriginalName !=null) {
                FileInformation fromFileInformation = fileMapper.selectByFileId(fileMd5);
                if (fromFileInformation == null) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 获取md5标识，并赋给fileMd5字段，避免重复使用函数
     * @param file 传入文件
     * @return md5标识字符串
     */
    private String getFileMd5(MultipartFile file) {
        byte[] buffer = new byte[1024];
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("md5");
            InputStream inputStream = file.getInputStream();
            BufferedInputStream bis = new BufferedInputStream(inputStream);
            int length = bis.read(buffer, 0, buffer.length);
            if (length > 0) {
                messageDigest.update(buffer);
                fileMd5 = new BigInteger(1, messageDigest.digest()).toString(16);
                return fileMd5;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取文件类型字符串，并赋给fileType字段，避免重复使用函数
     * @param file 传入文件
     * @return 文件类型字符串
     */
    private String getFileType(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        if (originalFilename != null) {
            //判断上传文件的名称是否符合文件名的正则表达式
            if (originalFilename.matches(RegexUtil.FILE_TYPE)) {
                fileType =  originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
                return fileType;
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * 获取文件原始名称字符串，不包含文件类型，方便页面展示
     * @param file 传入文件
     * @return 文件名称字符串
     */
    private String getFileOriginalName(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        if (originalFilename!= null) {
            //判断上传文件的名称是否符合文件名的正则表达式
            if (originalFilename.matches(RegexUtil.FILE_TYPE)) {
                fileOriginalName = originalFilename.substring(0,originalFilename.lastIndexOf("."));
                return fileOriginalName;
            } else {
                return null;
            }
        }
        return null;
    }

}
